#include "gkt_defines.h"
#include "gkt_os.h"
#include "gkt_board.h"
#include "gkt_driver.h"
#include "gkt_gpio.h"
//#include "gkt_rtc.h"
#include "gkt_debug.h"
#include "gkt_voice.h"

uint32_t g_backboard_power_up_time = 0;
static void driver_device_power_up(void)
{
	int retval;

	gkt_info("device_power_up: ...\n");

#ifdef GKT_CONFIG_RFID_BACKLIGHT_CTL_GPIO_PIN
	retval = gkt_gpio_config(GKT_CONFIG_RFID_BACKLIGHT_CTL_GPIO_PIN,
					GKT_GPIO_F_OUTPUT | GKT_GPIO_F_OUTPUT_HIGH,
					NULL);
	if (retval != GKT_SUCCESS) {
		gkt_error("driver_device_power_up: config card backlight failed %d\n", retval);
	}
#endif
#if defined(GKT_CONFIG_VOICE_OUTPUT_OPAMP_EN_PIN)
	gkt_gpio_padmux_desc_s desc;
	if (GKT_CONFIG_VOICE_OUTPUT_OPAMP_EN_PIN == GKT_CONFIG_DEVICE_POWER_CTRL_PIN)
	{
#if defined(GKT_CONFIG_DAC_OUTPUT_P_PAD_PIN)
		desc.pin = GKT_CONFIG_DAC_OUTPUT_P_PAD_PIN;
		desc.func = ANALOG;
		gkt_gpio_padmux_config(&desc);
#endif
#if defined(GKT_CONFIG_DAC_OUTPUT_N_PAD_PIN)
		desc.pin = GKT_CONFIG_DAC_OUTPUT_N_PAD_PIN;
		desc.func = ANALOG;
		gkt_gpio_padmux_config(&desc);
#endif
	}
#endif

	//Note that the following power-on sequence cannot be adjusted. 2023-11-21 17:34:24 zjy
#ifdef GKT_CONFIG_DEVICE_POWER_CTRL_PIN
	retval = gkt_gpio_config(GKT_CONFIG_DEVICE_POWER_CTRL_PIN,
					GKT_GPIO_F_OUTPUT | GKT_GPIO_F_OUTPUT_HIGH,
					NULL);
	if (retval != GKT_SUCCESS) {
		gkt_error("device_power_ctrl: config DEVICE_POWER_CTRL failed %d\n", retval);
		//return;
	}
	gkt_gpio_set_pin_level(GKT_CONFIG_DEVICE_POWER_CTRL_PIN,
			GKT_CONFIG_DEVICE_POWER_CTRL_LEVEL);
#endif

#if defined(GKT_CONFIG_BACKBOARD_POWER_PIN) && GKT_CONFIG_BACKBOARD_ENABLE
	retval = gkt_gpio_config(GKT_CONFIG_BACKBOARD_POWER_PIN, GKT_GPIO_F_OUTPUT, NULL);
	if (retval != GKT_SUCCESS) {
		gkt_error("device_power_ctrl: config pin failed: %d\n", retval);
		//return;
	}
	retval = gkt_gpio_set_pin_level(GKT_CONFIG_BACKBOARD_POWER_PIN, 1);
	if (retval != GKT_SUCCESS) {
		gkt_error("device_power_ctrl: set pin level failed: %d\n", retval);
		//return;
	}
	g_backboard_power_up_time = gkt_systime_ms();
#endif
#if defined(GKT_CONFIG_FACE_ID_POWER_CTRL_PIN)
	retval = gkt_gpio_config(GKT_CONFIG_FACE_ID_POWER_CTRL_PIN,
				GKT_GPIO_F_OUTPUT,
				NULL);
	if (retval != GKT_SUCCESS) {
		gkt_error("device_power_ctrl: config face_id reset pin failed - %d\n", retval);
		//return;
	}
	retval = gkt_gpio_set_pin_level(GKT_CONFIG_FACE_ID_POWER_CTRL_PIN,
			GKT_CONFIG_FACE_ID_POWER_CTRL_PIN_ENABLE);
	if (retval != GKT_SUCCESS) {
		gkt_error("device_power_ctrl: set face_id pin level failed: %d\n", retval);
		//return;
	}
#endif

#if defined(GKT_CONFIG_VOICE_OUTPUT_OPAMP_EN_PIN)
	gkt_info("device power up compatible voice\n");

	if (GKT_CONFIG_VOICE_OUTPUT_OPAMP_EN_PIN != GKT_CONFIG_DEVICE_POWER_CTRL_PIN) {
#if defined(GKT_CONFIG_DAC_OUTPUT_P_PAD_PIN)
		desc.pin = GKT_CONFIG_DAC_OUTPUT_P_PAD_PIN;
		desc.func = ANALOG;
		gkt_gpio_padmux_config(&desc);
#endif
#if defined(GKT_CONFIG_DAC_OUTPUT_N_PAD_PIN)
		desc.pin = GKT_CONFIG_DAC_OUTPUT_N_PAD_PIN;
		desc.func = ANALOG;
		gkt_gpio_padmux_config(&desc);
#endif
		uint32_t i;
		for(i=0; i<10; i++) {
			gkt_thread_sleep(20);
		}
		gkt_voice_output_opamp_init();
		gkt_voice_output_enable_opamp();
	}
#endif
}
static void driver_device_power_down(void)
{
#ifdef GKT_CONFIG_RFID_BACKLIGHT_CTL_GPIO_PIN
	gkt_gpio_set_pin_level(GKT_CONFIG_RFID_BACKLIGHT_CTL_GPIO_PIN, 0);
#endif
#ifdef GKT_CONFIG_DEVICE_POWER_CTRL_PIN
	int retval;
	if (GKT_CONFIG_DEVICE_POWER_CTRL_LEVEL)	{
		retval = gkt_gpio_set_pin_level(GKT_CONFIG_DEVICE_POWER_CTRL_PIN, 0);
		if (retval != GKT_SUCCESS) 
			gkt_error("driver_device_power_down: set level==0 failed: %d\n", retval);
	}
	else {	
		retval = gkt_gpio_config(GKT_CONFIG_DEVICE_POWER_CTRL_PIN, 
							GKT_GPIO_F_OUTPUT|GKT_GPIO_F_OUTPUT_HIGH, NULL);
		if (retval != GKT_SUCCESS) 
			gkt_error("driver_device_power_down: config POWER CTRL Pin failed: %d\n", retval);
	}
#endif
}
void gkt_driver_device_power_down(void)
{
	driver_device_power_down();
}


#ifdef GKT_CONFIG_DEVICE_MAXNUMS
#define __DEV_MAXNUMS	GKT_CONFIG_DEVICE_MAXNUMS
#else
#define __DEV_MAXNUMS	64
#endif
#define __DEV_MASK_WORDS	((__DEV_MAXNUMS + 31) >> 5)

#if defined(GKT_CONFIG_DEBUG_ENABLE) && GKT_CONFIG_DEBUG_ENABLE
#define __DEVICE_DEPEND_DEPTH_MAX	8
static uint32_t s_device_depend_depth = 0;
#define device_depend_depth_init()	\
	do { s_device_depend_depth = 0; } while(0)
#define device_depend_depth_check()	\
	do {	\
		if (s_device_depend_depth >= __DEVICE_DEPEND_DEPTH_MAX) {	\
			gkt_error("device_depend depth overflow!\n");	\
			do {} while(1);	\
		};	\
	} while(0)
#define device_depend_depth_increase()	\
	do {	\
		s_device_depend_depth++;	\
		device_depend_depth_check();	\
	} while(0)
#define device_depend_depth_decrease()	\
	do { s_device_depend_depth--; } while(0)
#else
#define device_depend_depth_init()	do {} while(0)
#define device_depend_depth_check()	do {} while(0)
#define device_depend_depth_increase()	do {} while(0)
#define device_depend_depth_decrease()	do {} while(0)
#endif

extern uint32_t *__device_desc_normal_start__;
static const gkt_device_desc_s *sc_desc_normal_start = 
	(const gkt_device_desc_s *)&__device_desc_normal_start__;

void __WEAK before_normal_device_driver_init(void)
{
	/* dummy */
}

extern uint32_t *__device_desc_external_start__;
static const gkt_device_desc_s *sc_desc_external_start = 
	(const gkt_device_desc_s *)&__device_desc_external_start__;
static int driver_init_do(const gkt_device_desc_s *start, 
				const gkt_device_desc_s *end, 
				const gkt_device_desc_s *curr,
				uint32_t *mask)
{
	const gkt_device_desc_s *const *depend;
	uint32_t word_idx, bit_idx;
	int retval;

	if (curr->depend_devs) {
		device_depend_depth_increase();

		depend = curr->depend_devs;
		while(*depend) {
			retval = driver_init_do(start, end, *depend, mask);
			if (retval) 
				return retval;
			depend++;
		}

		device_depend_depth_decrease();
	}

	if (curr == sc_desc_normal_start)
		before_normal_device_driver_init();
	else if (curr == sc_desc_external_start)
		driver_device_power_up();

	word_idx = (curr - start) >> 5;
	bit_idx = (curr - start) & 0x1f;
	if (!(mask[word_idx] & gkt_bit(bit_idx))) {
#if defined(GKT_CONFIG_DEBUG_ENABLE) && GKT_CONFIG_DEBUG_ENABLE
		gkt_info("driver_init: %s ...\n", curr->name_str);
#endif
		mask[word_idx] |= gkt_bit(bit_idx);
		if (curr->init) {
			retval = (*curr->init)();
			if (retval)
				return retval;
		}
	}

	return GKT_SUCCESS;
}

void __WEAK gkt_rtc_lp_check(void) {}

static gkt_device_rtc_alarm_fn s_rtc_alarm = NULL;
void gkt_driver_register_rtc_alarm(gkt_device_rtc_alarm_fn rtc_alarm)
{
	s_rtc_alarm = rtc_alarm;
}

extern uint32_t *__device_lp_hook_start__;
extern uint32_t *__device_lp_hook_end__;
void gkt_driver_lphook(int when, int mode)
{
#if defined(__GNUC__)
	const gkt_device_lp_hook_fn *lp_hook;
	const gkt_device_lp_hook_fn *lp_hook_end;

	lp_hook = (const gkt_device_lp_hook_fn *)&__device_lp_hook_start__;
	lp_hook_end = (const gkt_device_lp_hook_fn *)&__device_lp_hook_end__;
	while (lp_hook < lp_hook_end) {
		(*lp_hook)(when, mode);
		lp_hook++;
	}

#if defined(GKT_CONFIG_DEVICE_POWER_CTRL_DOWN_DELAYED) \
	&& GKT_CONFIG_DEVICE_POWER_CTRL_DOWN_DELAYED
	driver_device_power_down();
#endif

	gkt_rtc_lp_check();

	if (NULL != s_rtc_alarm)
		(*s_rtc_alarm)();
#endif
}

extern uint32_t *__device_desc_start__;
extern uint32_t *__device_desc_end__;
int gkt_driver_init(void)
{
#if defined(__GNUC__)
	const gkt_device_desc_s *descs_start;
	const gkt_device_desc_s *descs_end;
	const gkt_device_desc_s *desc;
	uint32_t mask[__DEV_MASK_WORDS], i;

	if (GKT_SYSTEM_RESET_HARD == g_system_info.reset_mode) {
		for (i=0; i<50; i++) {
			gkt_thread_sleep(10);
		}
	}
	
	for (i = 0; i < __DEV_MASK_WORDS; i++)
		mask[i] = 0;

	descs_start = (const gkt_device_desc_s *)&__device_desc_start__;
	descs_end = (const gkt_device_desc_s *)&__device_desc_end__;

	desc = descs_start;
	device_depend_depth_init();
	while (desc < descs_end) {
		driver_init_do(descs_start, descs_end, desc, mask);
		desc++;
	}
#endif

	return GKT_SUCCESS;
}

static gkt_driver_lp_mode_e s_driver_lp_mode = GKT_DRIVER_LP_NORMAL;
void gkt_driver_set_lp_mode(gkt_driver_lp_mode_e lp_mode)
{
	if (lp_mode < GKT_DRIVER_LP_MAX)
		s_driver_lp_mode = lp_mode;
}

gkt_driver_lp_mode_e gkt_driver_get_lp_mode(void)
{
	return s_driver_lp_mode;
}

